package tinyuser.acl;

import java.io.Serializable;
import java.security.Principal;
import java.security.acl.Permission;
import java.util.LinkedList;
import java.util.List;

/**
 * Access Control List that only uses positive permissions. Java's default ACL is too complicated using both positive
 * and negative permissions. This type of ACL assumes a permission is denied, unless it is present, in which case it is
 * granted.
 * 
 * @author Martin Algesten
 * 
 */
@SuppressWarnings("serial")
public class SimpleAcl implements Serializable {

	private String name;
	private List<SimpleAclEntry> entries = new LinkedList<SimpleAclEntry>();



	public SimpleAcl() {
	}



	/**
	 * Constructs a new ACL giving it a name.
	 * 
	 * @param name
	 *          The name of the ACL.
	 */
	public SimpleAcl(String name) {
		if (name == null)
			throw new IllegalArgumentException("name can't be null");
		this.name = name;
	}



	/**
	 * Constructs a new ACL setting the entries of the ACL.
	 * 
	 * @param name
	 *          The name of the acl.
	 * @param entries
	 *          The granted entries in the ACL.
	 */
	public SimpleAcl(String name, List<SimpleAclEntry> entries) {
		this(name);
		if (entries == null)
			throw new IllegalArgumentException("entries can't be null");
		this.entries.addAll(entries);
	}



	/**
	 * Returns the name of the ACL.
	 */
	public String getName() {
		return name;
	}



	protected void setName(String name) {
		this.name = name;
	}



	/**
	 * Grants a permission to a principal.
	 * 
	 * @param principal
	 *          The principal to grant a permission to.
	 * @param perm
	 *          The permission to grant.
	 * @return True if the operation was successful, false if the permission was already granted.
	 */
	public boolean addAllowed(Principal principal, Permission perm) {

		if (principal == null)
			throw new IllegalArgumentException("principal can't be null");

		if (perm == null)
			throw new IllegalArgumentException("perm can't be null");

		SimpleAclEntry entry = new SimpleAclEntry(principal, perm);

		if (entries.contains(entry))
			return false;

		return entries.add(entry);

	}



	/**
	 * Returns the entry for the given principal/permission combination.
	 * 
	 * @param principal
	 *          The principal to get the entry for
	 * @param perm
	 *          The permission to get the entry for.
	 * @return The entry, or null if not found.
	 */
	public SimpleAclEntry getSimpleAclEntry(Principal principal, Permission perm) {

		if (principal == null)
			throw new IllegalArgumentException("principal can't be null");

		if (perm == null)
			throw new IllegalArgumentException("perm can't be null");

		for (SimpleAclEntry e : entries) {

			if (e.principal().equals(principal) && e.permission().equals(perm))
				return e;

		}

		return null;

	}



	/**
	 * Revokes a granted privilege for the given principal/permission combination.
	 * 
	 * @param principal
	 *          The principal to revoke a permission for.
	 * @param perm
	 *          The permission to revoke.
	 * @return True if the permission was revoked, false if the permission wasn't granted.
	 */
	public boolean removeAllowed(Principal principal, Permission perm) {

		// arguments checked in getSimpleAclEntry

		SimpleAclEntry e = getSimpleAclEntry(principal, perm);

		if (e == null)
			return false;

		return entries.remove(e);

	}



	/**
	 * Tells if the permission is granted for any of the given principal(s).
	 * 
	 * @param perm
	 *          The permission to check.
	 * @param principals
	 *          The principal(s) to check for.
	 * @return If any of the given principals has the given permission granted.
	 */
	public boolean isAllowed(Permission perm, Principal... principals) {

		if (perm == null)
			throw new IllegalArgumentException("perm can't be null");

		if (principals == null)
			throw new IllegalArgumentException("principals can't be null");

		for (Principal p : principals) {

			if (getSimpleAclEntry(p, perm) != null)
				return true;

		}

		return false;

	}



	/**
	 * Returns the granted permissions.
	 */
	public List<SimpleAclEntry> entries() {

		return new LinkedList<SimpleAclEntry>(entries);

	}



	protected List<SimpleAclEntry> getEntries() {
		return entries;
	}



	protected void setEntries(List<SimpleAclEntry> entries) {
		this.entries = entries;
	}

}
